#ifndef tinyOSC_h
#define tinyOSC_h

#include "Arduino.h"
#include "WString.h"

#define MAX_DATA_STACK 2    // max number of numeric items in incoming message
#define MAX_DATA_BYTES 8    // bytes to store data, set to 4 * MAX_DATA_STACK
#define MAX_DATA_START 5    // test to keep from reading beyond top of stack
                            // set to MAX_DATA_BYTES - 3

/* 
This class implements a simple OSC compliant interface for a microcontroller
The OSC (Open Sound Control) specification can be found at opensoundcontrol.org

This implementation assumes that OSC communication is via a uart to an
external WiFi/Ethernet bridge.  A terminal character is added to the OSC serial
stream to flag the end of the OSC UDP message.  The OSC link may also be implemented
over a direct uart-Serial link, although this will generally require a specialized 
OSC client/server on the remote end (i.e. not UDP based).  In this case, it is
useful to set the terminal character to '\n' to allow the remote server to read
each OSC message as a line input.

The uart/terminal character assumptions impose some severe restrictions on the OSC
interface - particularly the "terminal character" can't appear in the outgoing data.
Because of this, tinyOSC is centered on sending all data as Strings, although some 
the capability to send one or two bytes of data as integers is supported. Care should
be taken to avoid sending the terminal character.

Input data can be any OSC format - String, integer, or float.  To minimize memory usage,
each OSC message is limited to one string and 2 numeric elements (float or int),
although this may be changed by editing the #define for MAX_DATA_STACK, MAX_DATA_BYTES,
and MAX_DATA_START.  

*/

class tinyOSC
{
  public:
    tinyOSC(void);
    void begin(Stream &theUart);             // Starts tinyOSC on serial port uart
    void begin(Stream &theUart, byte end_char);  // Starts tinyOSC, sets termination char to end_char
    boolean available(void);              // True if new OSC message available (Processes serial buffer)
    String containerList;                 // The container list from last OSC message
    String method;                        // The method from the OSC message
    String dataString;                    // The data string if sent in OSC message
    int num_data;                         // The number of data items in the OSC message
    byte term_char;                       // The termination character for outgoing OSC messages
    //unsigned long packet_delay;
    char dataType[MAX_DATA_STACK];        // A char array of OSC message data types: {'i','s','f'}
    int iPop(void);                       // Pops value off numeric stack as integer - convert if req'd
    float fPop(void);                     // Pops value off numeric stack as float - convert if req'd
    float pop_float(void);                // Pops 4 bytes off stack as float - no conversion
    long pop_long(void);                  // Pops 4 bytes off stack as long - no conversion
    void sendCmd(char *OSCstring);        // Sends OSCstring as OSC command ("/{str}" + padding & ',')
    void sendString(char *OSCstring, String dataString);  // Sends dataString with OSCsring cmd 
                                                          // (/{OSCstr},s {dataString})
    void sendMsg(char *OSCstring);        // Sends current message with OSC command {OSCstring}
    void sendByte(char *OSCstring, byte OSCbyte);         // Sends OSCbyte as integer message
    void send2Byte(char *OSCstring, byte byte1, byte byte2);  // Sends two bytes as two integer message
    void clearMsg(void);                  // Clears current message
    void addMsg(int msgInt);              // Adds msgInt to current message as a String
    void addMsg(String msgStr);           // Adds msgStr to current message String
    void addMsg(char msgChar);            // Adds msgChar to current message String
    void addMsg(char *msgChar);           // Adds msgChar[] to current message String
    Stream* uart;                         // The serial port object

  private:
    char _start;
    byte _nill;
    int _zero;
    int _wordSize;
    void _sendStr(char *OSCstring);
    void _sendEnd(void);
    void _init(void);
    byte _dataStack[MAX_DATA_BYTES];
    int _stackPtr;
    int _parseMode;
    int _dataCtr;
    int _flushCtr;
    String _msgString;
    String _nillStr;
    //unsigned long _time;
};

#endif